from sqlalchemy.orm import Session
from typing import List, Dict, Tuple, Optional
from ..models.models import Student, Preference, Interest
from ..config import settings
import numpy as np
from collections import defaultdict

class RoommateMatchingService:
    def __init__(self, db: Session):
        self.db = db
        
    def calculate_schedule_compatibility(self, student1: Student, student2: Student) -> float:
        """计算作息时间的兼容性分数 (0-30分)"""
        if not student1.sleeping_habit or not student2.sleeping_habit:
            return 15.0  # 如果未设置作息时间，返回中等分数
            
        schedule_map = {
            "EARLY": 0,
            "NORMAL": 1,
            "LATE": 2
        }
        
        # 获取枚举值的字符串表示
        habit1 = student1.sleeping_habit.value if hasattr(student1.sleeping_habit, 'value') else student1.sleeping_habit
        habit2 = student2.sleeping_habit.value if hasattr(student2.sleeping_habit, 'value') else student2.sleeping_habit
        
        diff = abs(schedule_map[habit1] - schedule_map[habit2])
        if diff == 0:
            return 30.0
        elif diff == 1:
            return 15.0
        else:
            return 0.0
            
    def calculate_cleanliness_compatibility(self, student1: Student, student2: Student) -> float:
        """计算卫生习惯的兼容性分数 (0-30分)"""
        if not student1.cleanliness or not student2.cleanliness:
            return 15.0  # 如果未设置卫生习惯，返回中等分数
            
        cleanliness_map = {
            "VERY_CLEAN": 3,
            "CLEAN": 2,
            "NORMAL": 1,
            "CASUAL": 0
        }
        
        # 获取枚举值的字符串表示
        clean1 = student1.cleanliness.value if hasattr(student1.cleanliness, 'value') else student1.cleanliness
        clean2 = student2.cleanliness.value if hasattr(student2.cleanliness, 'value') else student2.cleanliness
        
        diff = abs(cleanliness_map[clean1] - cleanliness_map[clean2])
        return max(0, 30 - diff * 10)  # 差异每级降低10分
            
    def calculate_interest_compatibility(self, student1: Student, student2: Student) -> float:
        """计算兴趣爱好的兼容性分数 (0-40分)"""
        interests1 = set(interest.id for interest in student1.interests)
        interests2 = set(interest.id for interest in student2.interests)
        
        if not interests1 or not interests2:
            return 20.0  # 如果任一方没有设置兴趣爱好，返回中等分数
            
        common_interests = interests1.intersection(interests2)
        return min(len(common_interests) * 8, 40)  # 每个共同兴趣8分，最高40分
        
    def calculate_preference_score(self, student1: Student, student2: Student) -> float:
        """计算互相偏好分数 (0-20分)"""
        pref1 = self.db.query(Preference).filter(
            Preference.student_id == student1.id,
            Preference.preferred_student_id == student2.id
        ).first()
        
        pref2 = self.db.query(Preference).filter(
            Preference.student_id == student2.id,
            Preference.preferred_student_id == student1.id
        ).first()
        
        score = 0
        if pref1:
            score += pref1.preference_level * 2  # 每级偏好2分
        if pref2:
            score += pref2.preference_level * 2
            
        return min(score, 20)  # 最高20分
        
    def calculate_compatibility_score(self, student1: Student, student2: Student) -> float:
        """
        计算总体兼容性分数 (0-100分)
        - 作息时间：30分
        - 卫生习惯：30分
        - 兴趣爱好：40分
        - 互选偏好：20分（额外加分项）
        """
        print(f"Calculating compatibility between {student1.name} and {student2.name}")
        print(f"Student1 habits: sleeping={student1.sleeping_habit}, cleanliness={student1.cleanliness}")
        print(f"Student2 habits: sleeping={student2.sleeping_habit}, cleanliness={student2.cleanliness}")
        
        if student1.gender != student2.gender:
            print("Gender mismatch")
            return 0.0  # 不同性别学生不能住在一起
            
        # 获取学生的偏好设置
        student1_pref = self.db.query(Preference).filter(Preference.student_id == student1.id).first()
        student2_pref = self.db.query(Preference).filter(Preference.student_id == student2.id).first()
        
        print(f"Student1 preferences: {student1_pref}")
        print(f"Student2 preferences: {student2_pref}")
        
        # 如果任一学生没有设置偏好，返回0分
        if not student1_pref or not student2_pref:
            print("Missing preferences")
            return 0.0
            
        # 计算作息时间匹配分（30%）
        schedule_score = self.calculate_schedule_compatibility(student1, student2)
        
        # 计算卫生习惯匹配分（30%）
        cleanliness_score = self.calculate_cleanliness_compatibility(student1, student2)
        
        # 计算兴趣标签重合度（40%）
        student1_tags = set(student1_pref.interest_tags or [])
        student2_tags = set(student2_pref.interest_tags or [])
        print(f"Student1 tags: {student1_tags}")
        print(f"Student2 tags: {student2_tags}")
        
        common_interests = student1_tags & student2_tags
        print(f"Common interests: {common_interests}")
        
        interest_score = min(len(common_interests) * 5, 40)  # 每个共同兴趣5分，最高40分
        
        # 计算基础匹配分（100分制）
        base_score = schedule_score + cleanliness_score + interest_score
        
        # 计算互选偏好加分（最多20分的额外加分）
        preference_score = self.calculate_preference_score(student1, student2)
        
        print(f"Scores - schedule: {schedule_score}, cleanliness: {cleanliness_score}, interest: {interest_score}, preference: {preference_score}")
        
        # 最终分数 = 基础分 + 互选偏好加分（上限100分）
        final_score = min(base_score + preference_score, 100)
        print(f"Final compatibility score: {final_score}")
        
        return final_score
        
    def find_matches(self, student: Student, min_score: float = 60.0) -> List[Dict]:
        """为指定学生找到合适的室友匹配"""
        matches = []
        print(f"Finding matches for student: {student.name} (ID={student.id})")
        
        # 获取所有潜在的室友候选人（同性别，排除自己）
        candidates = (
            self.db.query(Student)
            .filter(
                Student.gender == student.gender,  # 确保性别相同
                Student.id != student.id,
                Student.is_active == True  # 只匹配激活的学生
            )
            .all()
        )
        print(f"Found {len(candidates)} potential candidates")
        
        # 获取当前学生的偏好设置
        student_pref = self.db.query(Preference).filter(Preference.student_id == student.id).first()
        if not student_pref or not student_pref.interest_tags:
            print("Current student has no preferences or interest tags")
            return []
            
        # 计算每个候选人的匹配分数
        for candidate in candidates:
            print(f"Calculating score for candidate: {candidate.name} (ID={candidate.id})")
            score = self.calculate_compatibility_score(student, candidate)
            print(f"Score for {candidate.name}: {score}")
            
            if score >= min_score:
                # 获取共同兴趣标签
                candidate_pref = self.db.query(Preference).filter(Preference.student_id == candidate.id).first()
                student_tags = set(student_pref.interest_tags or [])
                candidate_tags = set(candidate_pref.interest_tags or [])
                common_interests = list(student_tags & candidate_tags)
                
                matches.append({
                    "student": candidate,
                    "score": score,
                    "common_interests": common_interests,
                    "match_details": {
                        "schedule_score": self.calculate_schedule_compatibility(student, candidate),
                        "cleanliness_score": self.calculate_cleanliness_compatibility(student, candidate),
                        "interest_score": min(len(common_interests) * 5, 40),
                        "preference_score": self.calculate_preference_score(student, candidate)
                    }
                })
        
        # 按匹配分数降序排序
        matches.sort(key=lambda x: x["score"], reverse=True)
        print(f"Found {len(matches)} matches with score >= {min_score}")
        return matches
        
    def get_top_matches(self, student: Student, top_n: int = 5) -> List[Dict]:
        """获取匹配度最高的前N个候选人"""
        matches = self.find_matches(student)
        return matches[:top_n]
        
    def generate_roommate_groups(self, students: List[Student], room_size: int = 4) -> List[List[Student]]:
        """生成最优的室友分组"""
        unassigned_students = [s for s in students if not s.room_assignment]
        groups = []
        
        while unassigned_students:
            current_group = [unassigned_students.pop(0)]
            
            while len(current_group) < room_size and unassigned_students:
                # 为当前组找到最佳匹配的下一个室友
                best_match = None
                best_score = -1
                
                for candidate in unassigned_students:
                    avg_score = sum(
                        self.calculate_compatibility_score(member, candidate)
                        for member in current_group
                    ) / len(current_group)
                    
                    if avg_score > best_score:
                        best_score = avg_score
                        best_match = candidate
                
                if best_match and best_score >= 60:  # 设置最低兼容性阈值为60分
                    current_group.append(best_match)
                    unassigned_students.remove(best_match)
                else:
                    break
                    
            groups.append(current_group)
            
        return groups

    def create_roommate_preference(
        self,
        student_id: int,
        preferred_student_id: int,
        preference_level: int
    ) -> Preference:
        """创建室友偏好记录"""
        # 检查是否已存在偏好记录
        existing_pref = (
            self.db.query(Preference)
            .filter(
                Preference.student_id == student_id,
                Preference.preferred_student_id == preferred_student_id
            )
            .first()
        )
        
        if existing_pref:
            existing_pref.preference_level = preference_level
            self.db.commit()
            self.db.refresh(existing_pref)
            return existing_pref
            
        preference = Preference(
            student_id=student_id,
            preferred_student_id=preferred_student_id,
            preference_level=preference_level
        )
        
        self.db.add(preference)
        self.db.commit()
        self.db.refresh(preference)
        return preference
        
    def get_mutual_preferences(self, student_id: int) -> List[Tuple[Student, float]]:
        """获取互相选择的室友及其匹配分数"""
        mutual_prefs = []
        
        # 获取该学生的所有偏好
        student_prefs = (
            self.db.query(Preference)
            .filter(Preference.student_id == student_id)
            .all()
        )
        
        for pref in student_prefs:
            # 检查是否存在互相选择
            mutual_pref = (
                self.db.query(Preference)
                .filter(
                    Preference.student_id == pref.preferred_student_id,
                    Preference.preferred_student_id == student_id
                )
                .first()
            )
            
            if mutual_pref:
                preferred_student = (
                    self.db.query(Student)
                    .filter(Student.id == pref.preferred_student_id)
                    .first()
                )
                
                if preferred_student:
                    # 计算匹配分数
                    score = self.calculate_compatibility_score(
                        self.db.query(Student).get(student_id),
                        preferred_student
                    )
                    mutual_prefs.append((preferred_student, score))
                    
        # 按匹配分数降序排序
        mutual_prefs.sort(key=lambda x: x[1], reverse=True)
        return mutual_prefs 